﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestVisioCanvas.Helpers;

using Visio = Microsoft.Office.Interop.Visio;


namespace TestVisioCanvas.Wrappers
{
    /// <summary>
    /// Class that represents a Shape managed inside the (A)ISS
    /// </summary>
    public class ManagedShape : Models.BaseNotify, IShape, ITranslatable, IResizable
    {
        private string _name;
        private int _shapeID;
        private double _width, _height;
        private double _angle;
        /// <summary>
        /// Centroid coordinates
        /// </summary>
        private System.Windows.Point _pin;
        private Visio.Shape _shape;
        public event ShapeTransformedEventHandler ShapeTransformed;
        private static System.Windows.Point Zero = new System.Windows.Point(0, 0);

        /// <summary>
        /// Public constructor of the Managed Shape
        /// </summary>
        /// <param name="Shape">The starting standard Visio Shape</param>
        public ManagedShape(Visio.Shape Shape)
        {
            _shape = Shape;
            _name = Shape.NameU;
            _width = Shape.CellsU["Width"].get_Result(StaticUtils.VisioCentimeters);
            _height = Shape.CellsU["Height"].get_Result(StaticUtils.VisioCentimeters);
            _angle = Shape.CellsU["Angle"].get_Result(StaticUtils.VisioCentimeters);
            _pin = new System.Windows.Point(
                Shape.CellsU["PinX"].get_Result(StaticUtils.VisioCentimeters),
                Shape.CellsU["PinY"].get_Result(StaticUtils.VisioCentimeters));
            _shapeID = Shape.ID;

            // prevents to modify the text
            Shape.CellsU["LockTextEdit"].set_Result(Visio.VisUnitCodes.visNoCast, 1);
            this.VisioShape.CellsU["LockWidth"].set_Result(Visio.VisUnitCodes.visNoCast, 0);
            this.VisioShape.CellsU["LockHeight"].set_Result(Visio.VisUnitCodes.visNoCast, 0);
            this.VisioShape.CellsU["LockAspect"].set_Result(Visio.VisUnitCodes.visNoCast, 0);
        }

        /// <summary>
        /// The name of the shape
        /// </summary>
        public String Name { get { return _name; } }

        /// <summary>
        /// Raises the ShapeTransformed event
        /// </summary>
        /// <param name="DeltaTranslate">Delta of the translation transform</param>
        /// <param name="DeltaResize">Delta of the resize transform</param>
        private void OnShapeTransformed(System.Windows.Point DeltaTranslate, System.Windows.Point DeltaResize)
        {
            if (ShapeTransformed != null)
            {
                ShapeTransformed(this, new ShapeTransformedEventArgs(DeltaTranslate, DeltaResize));
            }
        }

        /// <summary>
        /// Width property of the shape
        /// </summary>
        public double Width
        {
            get { return _width; }
            set
            {
                if (value > 0)
                {
                    double old = _width;
                    _width = value;
                    UpdateProperty(ShapeProperty.Width, _width);
                    OnShapeTransformed(Zero, new System.Windows.Point(value - old, 0));
                }
                else
                {
                    throw new ArgumentException("Width cannot be less than or equal to zero");
                }
            }
        }

        /// <summary>
        /// Height property of the shape
        /// </summary>
        public double Height
        {
            get { return _height; }
            set
            {
                if (value > 0)
                {
                    double old = _height;
                    _height = value;
                    UpdateProperty(ShapeProperty.Height, _height);
                    OnShapeTransformed(Zero, new System.Windows.Point(0, value - old));
                }
                else
                {
                    throw new ArgumentException("Height cannot be less than or equal to zero");
                }
            }
        }

        /// <summary>
        /// Angle property of the shape
        /// </summary>
        public double Angle
        {
            get { return _angle; }
            set
            {                
                double old = _angle;
                _angle = value;
                UpdateProperty(ShapeProperty.Angle, _angle);
                    //OnShapeTransformed(Zero, new System.Windows.Point(value - old, 0));                
            }
        }

        /// <summary>
        /// Centroid property of the shape
        /// </summary>
        public System.Windows.Point Pin
        {
            get { return _pin; }
            set
            {
                if (value != null)
                {
                    var old = _pin;
                    _pin = value;
                    UpdateProperty(ShapeProperty.PinX, _pin.X);
                    UpdateProperty(ShapeProperty.PinY, _pin.Y);
                    OnShapeTransformed(new System.Windows.Point(value.X - old.X, value.Y - old.Y),
                        Zero);
                }
            }
        }

        /// <summary>
        /// Centroid's X coordinate
        /// </summary>
        public double PinX
        {
            get { return _pin.X; }
            set
            {
                var old = _pin.X;
                _pin.X = value;
                UpdateProperty(ShapeProperty.PinX, _pin.X);
                OnShapeTransformed(new System.Windows.Point(value - old, 0),
                    Zero);
            }
        }

        /// <summary>
        /// Centroid's Y coordinate
        /// </summary>
        public double PinY
        {
            get { return _pin.Y; }
            set
            {
                var old = _pin.Y;
                _pin.Y = value;
                UpdateProperty(ShapeProperty.PinY, _pin.Y);
                OnShapeTransformed(new System.Windows.Point(value - old, 0), Zero);
            }
        }

        /// <summary>
        /// Translation method
        /// </summary>
        /// <param name="OffsetX">Offset for the X coordinate</param>
        /// <param name="OffsetY">Offset for the Y coordinate</param>
        /// <returns></returns>
        public System.Windows.Point Translate(double OffsetX, double OffsetY)
        {
            _pin.X += OffsetX;
            _pin.Y += OffsetY;
            UpdateProperty(ShapeProperty.PinX, _pin.X);
            UpdateProperty(ShapeProperty.PinY, _pin.Y);

            OnShapeTransformed(new System.Windows.Point(OffsetX, OffsetY), Zero);
            return _pin;
        }

        /// <summary>
        /// Resize method
        /// </summary>
        /// <param name="NewWidth">the new width</param>
        /// <param name="NewHeight">the new height</param>
        /// <returns></returns>
        public System.Windows.Point Resize(double NewWidth, double NewHeight)
        {
            double oldWidth = _width, oldHeight = _height;
            _width = NewWidth;
            _height = NewHeight;
            UpdateProperty(ShapeProperty.Width, _width);
            UpdateProperty(ShapeProperty.Height, _height);
            OnShapeTransformed(Zero, new System.Windows.Point(_width - oldHeight, _height - oldHeight));
            return new System.Windows.Point(_width, _height);
        }

        /// <summary>
        /// Property that exposes the ID of the shape
        /// </summary>
        public int ID
        {
            get { return _shapeID; }
        }

        /// <summary>
        /// Property that exposes the standard visio shape
        /// </summary>
        public Visio.Shape VisioShape
        {
            get { return _shape; }
        }


        public void SynchronizeProperty(string PropertyName, double value)
        {
            if (PropertyName == "Angle")
            {
                this._angle = value;
            }
            if (PropertyName == "PinX")
                this._pin.X = value;

            if (PropertyName == "PinY")
                this._pin.Y = value;

            if (PropertyName == "Width")
                this._width = value;

            if (PropertyName == "Height")
                this._height = value;
        }
        /// <summary>
        /// Method for properties update.
        /// </summary>
        /// <param name="PropertyName">The enumeration value that represents a single property</param>
        /// <param name="value">The new value for the property</param>
        /// <returns>True if the property was updated, false if not</returns>
        protected bool UpdateProperty(ShapeProperty PropertyName, double value)
        {
            try
            {
                switch (PropertyName)
                {
                    case ShapeProperty.PinX:                        
                        _shape.CellsU["PinX"].set_Result(StaticUtils.VisioCentimeters, value);
                        break;
                    case ShapeProperty.PinY:
                        _shape.CellsU["PinY"].set_Result(StaticUtils.VisioCentimeters, value);
                        break;
                    case ShapeProperty.Width:                        
                        _shape.CellsU["Width"].set_Result(StaticUtils.VisioCentimeters, value);
                        break;
                    case ShapeProperty.Height:
                        _shape.CellsU["Height"].set_Result(StaticUtils.VisioCentimeters, value);
                        break;
                    case ShapeProperty.Angle :
                        _shape.CellsU["Angle"].set_Result(StaticUtils.VisioCentimeters, value);
                        break;
                    default:
                        return false;
                }
                return true;
            }
            catch (System.Runtime.InteropServices.COMException c)
            {
#if DEBUG
                System.Diagnostics.Debug.WriteLine(c.Message);
#endif
                return false;
            }
        }
    }
}